package stone.ast;

import org.jetbrains.annotations.NotNull;
import runner.Environment;
import stone.StoneException;

import java.util.List;

import static runner.Environment.FALSE;
import static runner.Environment.TRUE;

public class BinaryExpr extends ASTList {
  public BinaryExpr(List<ASTree> list) {
    super(list);
  }

  public ASTree left() {
    return child(0);
  }

  public String operator() {
    return ((ASTLeaf) child(1)).getToken().getText();
  }

  public ASTree right() {
    return child(2);
  }

  @NotNull
  @Override
  public Object eval(Environment env) {
    String op = operator();
    if ("=".equals(op)) {
      Object right = right().eval(env);
      return computeAssign(env, right);
    } else {
      Object left = left().eval(env);
      Object right = right().eval(env);
      return computeOp(left, op, right);
    }
  }

  private Object computeAssign(Environment env, Object rvalue) {
    ASTree l = left();
    if (l instanceof Name) {
      env.put(((Name) l).name(), rvalue);
      return rvalue;
    } else
      throw new StoneException("bad assignment", this);
  }

  private Object computeOp(Object left, String op, Object right) {
    if (left instanceof Integer && right instanceof Integer) {
      return computeNumber((Integer) left, op, (Integer) right);
    } else if (op.equals("+"))
      return String.valueOf(left) + String.valueOf(right);
    else if (op.equals("==")) {
      if (left == null)
        return right == null ? TRUE : FALSE;
      else
        return left.equals(right) ? TRUE : FALSE;
    } else
      throw new StoneException("bad type", this);
  }

  private Object computeNumber(Integer left, @NotNull String op, Integer right) {
    int a = left;
    int b = right;
    switch (op) {
      case "+":
        return a + b;
      case "-":
        return a - b;
      case "*":
        return a * b;
      case "/":
        return a / b;
      case "%":
        return a % b;
      case "==":
        return a == b ? TRUE : FALSE;
      case ">":
        return a > b ? TRUE : FALSE;
      case "<":
        return a < b ? TRUE : FALSE;
      default:
        throw new StoneException("bad operator", this);
    }
  }
}
